package com.crm.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 基于Token的身份验证的主流程如下:
    用户通过用户名和密码发送请求；
    程序验证；
    程序返回一个签名的token 给客户端；
    客户端储存token,并且每次用于每次发送请求。
	JWT的三个部分
	1、JWT头
	 {
     "alg": "HS256",
    "typ": "JWT"
  }
	在上面的代码中，alg属性表示签名使用的算法，默认为HMAC SHA256（写为HS256）；
	typ属性表示令牌的类型，JWT令牌统一写为JWT。
	最后，使用Base64 URL算法将上述JSON对象转换为字符串保存

     2、有效载荷
	 有效载荷部分，是JWT的主体内容部分，也是一个JSON对象，包含需要传递的数据。
	 JWT指定七个默认字段供选择。
		iss：发行人
		exp：到期时间
		sub：主题
		aud：用户
		nbf：在此之前不可用
		iat：发布时间
		jti：JWT ID用于标识该JWT
		除以上默认字段外，我们还可以自定义私有字段，如下例：
		{
		"sub": "1234567890",
		"name": "chongchong",
		"admin": true
		}

	3、签名哈希
	签名哈希部分是对上面两部分数据签名，通过指定的算法生成哈希，以确保数据不会被篡改。
 */

/**
 * JSON Web Token 注意:需要JDK1.7以上 一。定义一个JsonWebToken的工具类，具有加密和解密token的功能
 * 
 * 需要依赖的两个Jar java-jwt-3.2.0.jar commons-codec-1.10.jar
 * 
 * 和任意一个json转换工具 jackson-annotations-2.6.1.jar jackson-core-2.6.1.jar
 * jackson-databind-2.6.1.jar
 * 
 */
@Component
public class JwtToken {

	// 密钥
	private static final String SECRET = "crm";

	// jackson
	private static ObjectMapper mapper = new ObjectMapper();

	/**
	 * 1、生成JWT头 header数据
	 * 
	 * @return
	 */
	private static Map<String, Object> createHeader() {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("typ", "JWT");
		map.put("alg", "HS256");
		return map;
	}

	/**
	 * 生成token 2、有效载荷
	 * 
	 * @param obj    对象数据
	 * @param maxAge 有效期（单位：毫秒）
	 * @param <T>
	 * @return
	 */
	public static <T> String sign(T obj, long maxAge) {
		JWTCreator.Builder builder = JWT.create();

		try {
			builder.withHeader(createHeader())// header
					.withSubject(mapper.writeValueAsString(obj));
			if (maxAge >= 0) {
				long expMillis = System.currentTimeMillis() + maxAge;
				Date exp = new Date(expMillis);
				builder.withExpiresAt(exp);
			}
			return builder.sign(Algorithm.HMAC256(SECRET));

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (JWTCreationException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 解密
	 * 
	 * @param token  token字符串
	 * @param classT 解密后的类型
	 * @param <T>
	 * @return
	 */
	public static <T> T unsign(String token, Class<T> classT) throws IOException {
		JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
		DecodedJWT jwt = verifier.verify(token);

		Date exp = jwt.getExpiresAt();
		if(exp!=null&&exp.after(new Date())){
			String subject = jwt.getSubject();
			return mapper.readValue(subject, classT);
		}

		return null;
	}


}
